package cn.felord.security.cloud.oauth2.resource.server.autoconfigure;

import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.*;

import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.interfaces.RSAPublicKey;
import java.time.Duration;
import java.util.Collection;

/**
 * The type Jwt decoder configuration.
 */
@EnableConfigurationProperties(ResourceServerProperties.class)
@ConditionalOnProperty(value = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri",matchIfMissing = true)
@Configuration(proxyBeanMethods = false)
public class JwtDecoderConfiguration {
    @Autowired
    private ResourceServerProperties resourceServerProperties;


    /**
     * Jwt issuer validator jwt issuer validator.
     *
     * @return the jwt issuer validator
     */
    @Bean
    JwtIssuerValidator jwtIssuerValidator() {
        return new JwtIssuerValidator(this.resourceServerProperties.getIssuer());
    }

    /**
     * Jwt timestamp validator jwt timestamp validator.
     *
     * @return the jwt timestamp validator
     */
    @Bean
    JwtTimestampValidator jwtTimestampValidator() {
        return new JwtTimestampValidator(Duration.ofSeconds((long) this.resourceServerProperties.getExpiresAt()));
    }

    /**
     * Delegating token validator delegating o auth 2 token validator.
     *
     * @param tokenValidators the token validators
     * @return the delegating o auth 2 token validator
     */
    @Primary
    @Bean({"delegatingTokenValidator"})
    public DelegatingOAuth2TokenValidator<Jwt> delegatingTokenValidator(Collection<OAuth2TokenValidator<Jwt>> tokenValidators) {
        return new DelegatingOAuth2TokenValidator<>(tokenValidators);
    }
    /**
     * Jwt decoder jwt decoder.
     *
     * @param validator the validator
     * @return the jwt decoder
     */
    @SneakyThrows
    @Bean
    @ConditionalOnProperty(prefix = "spring.cloud.oauth2.resource.server",name = "public-key-location")
    public JwtDecoder jwtDecoder(@Qualifier("delegatingTokenValidator") DelegatingOAuth2TokenValidator<Jwt> validator) {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        ClassPathResource resource = new ClassPathResource(this.resourceServerProperties.getPublicKeyLocation());
        Certificate certificate = certificateFactory.generateCertificate(resource.getInputStream());
        PublicKey publicKey = certificate.getPublicKey();
        NimbusJwtDecoder nimbusJwtDecoder = NimbusJwtDecoder.withPublicKey((RSAPublicKey) publicKey).build();
        nimbusJwtDecoder.setJwtValidator(validator);
        return nimbusJwtDecoder;
    }
}
